#pragma once

namespace Str
{

#define HAS_MEM_FN(func)				\
template<typename T, typename... Args>	\
struct has_mem_fn_##func				\
{										\
private:								\
	template<typename U>				\
	static auto Check(int) -> decltype(std::declval<U>().func(std::declval<Args>()...), true); \
	template<typename U>				\
	static void Check(...);				\
public:									\
	static constexpr bool value = !std::is_void_v<decltype(Check<T>(0))>; \
};

HAS_MEM_FN(c_str)

template<typename T>
struct is_su_ptr : std::false_type
{
};

template<typename T>
struct is_su_ptr<std::shared_ptr<T>> : std::true_type
{
};

template<typename T>
struct is_su_ptr<std::unique_ptr<T>> : std::true_type
{
};

template<typename Arg>
inline decltype(auto) TypeCast(Arg&& arg)
{
	using Ty = std::decay_t<Arg>;

	if constexpr (has_mem_fn_c_str<Ty>::value)
		return arg.c_str();
	else if constexpr (is_su_ptr<Ty>::value)
		return arg.get();
	else if constexpr (std::is_convertible_v<Ty, LPCSTR>)
		return (LPCSTR)arg;
	else if constexpr (std::is_convertible_v<Ty, LPCWSTR>)
		return (LPCWSTR)arg;
	else
		return std::forward<Arg>(arg);
}

template<typename Ch, typename Arg>
inline decltype(auto) TypeCheck(Arg&& arg)
{
	using Ty1 = std::decay_t<Arg>;
	static_assert(std::is_scalar_v<Ty1>, "Type must be scalar");

	if constexpr (std::is_pointer_v<Ty1>)
	{
		using Ty2 = std::remove_cv_t<std::remove_pointer_t<Ty1>>;

		if constexpr (std::is_same_v<Ty2, char> || std::is_same_v<Ty2, wchar_t>)
		{
			static_assert(std::is_same_v<Ty2, Ch>, "String pointer mismatch");
		}
	}

	return std::forward<Arg>(arg);
}

}	// namespace Str
